
#include <stdio.h>
#include <cassert>
#include <string.h>
#include <inttypes.h>
#include <math.h>

using namespace std;
#include "cbp3_def.h"
#include "cbp3_framework.h"


#define NUMBER_TABLES 12 //Number of tables
#define THRESHOLD 12 //Using rho = M is a good tradeoff

#define TABLE_SIZE 10664 //1333 4-bit entries * 12 tables * 8 = 64KBytes

#define ALPHA 2
#define BASE_L 2

int8_t *tables[NUMBER_TABLES];


uint32_t brh_fetch;
uint32_t brh_retire;


void PredictorInit() {
    //runs = 0;
  
}

void PredictorReset() {
    // this function is called before EVERY run
    // it is used to reset predictors and change configurations

  for (int i = 0; i < NUMBER_TABLES; i ++) {
        tables[i] = new int8_t[TABLE_SIZE];
	for (int j=0;j<TABLE_SIZE;j++) {
	  tables[i][j] = 0;
	}
  }
 
    brh_fetch = 0;
    brh_retire = 0;
}

//http://www.jilp.org/cbp/Andre.pdf

int length(int i) {

  return (int)(pow(ALPHA , i-1) * BASE_L + 0.5);

}

uint32_t select(uint32_t from, int nBits) {
  int n_ones = 3;
  for (int i=2; i<nBits;i++) 
    n_ones += (1 << i);
  
  return (from & n_ones);
  
}



void PredictorRunACycle() {
    // get info about what uops are processed at each pipeline stage
    const cbp3_cycle_activity_t *cycle_info = get_cycle_info();

    // make prediction at fetch stage
    for (int i = 0; i < cycle_info->num_fetch; i++) {
        uint32_t fe_ptr = cycle_info->fetch_q[i];
        const cbp3_uop_dynamic_t *uop = &fetch_entry(fe_ptr)->uop;

        if (/* runs == 0 && */uop->type & IS_BR_CONDITIONAL) {
           
	  //First table is indexed by the program counter:
	  int32_t pred = tables[0][uop->pc & (TABLE_SIZE-1)];
	  
	  for (int i=1;i<NUMBER_TABLES;i++)
	    pred += tables[i][select(brh_fetch,length(i)) & (TABLE_SIZE-1)];
	    
	  //make prediction:
	  pred += NUMBER_TABLES/2;
	  assert(report_pred(fe_ptr, false, (pred >= 0)));

	}else if (/* runs == 1 && */ uop->type & IS_BR_INDIRECT) {
            
        }

        // update fetch branch history
        if (uop->type & IS_BR_CONDITIONAL)
            brh_fetch = (brh_fetch << 1) | (uop->br_taken ? 1 : 0);
        else if (uop_is_branch(uop->type))
            brh_fetch = (brh_fetch << 1) | 1;
    }

    for (int i = 0; i < cycle_info->num_retire; i++) {
        uint32_t rob_ptr = cycle_info->retire_q[i];
        const cbp3_uop_dynamic_t *uop = &rob_entry(rob_ptr)->uop;

        if (/*runs == 0 &&*/ uop->type & IS_BR_CONDITIONAL) {
	  //get prediction:
	  int32_t pred = tables[0][uop->pc & (TABLE_SIZE-1)];
	  
	  for (int i=1;i<NUMBER_TABLES;i++)
	    pred += tables[i][select(brh_retire,length(i)) & (TABLE_SIZE-1)];
	    
	  pred += NUMBER_TABLES/2;
	  int32_t pred_abs = pred >= 0 ? pred : ((pred ^ -1) + 1);
	  //update predictor:
	  if ( ((pred <= 0) != uop->br_taken) || (pred_abs <= THRESHOLD)) {
	    if (uop->br_taken) 
	      tables[0][uop->pc & (TABLE_SIZE-1)]++;
	    else  
	      tables[0][uop->pc & (TABLE_SIZE-1)]--;

	    for (int i=1;i<NUMBER_TABLES;i++)
		if (uop->br_taken)
		  tables[i][select(brh_retire,length(i)) & (TABLE_SIZE-1)]++;
	        else
                  tables[i][select(brh_retire,length(i)) & (TABLE_SIZE-1)]--;
	          
	    

	    
	  }

        }else if (/*runs == 1 &&*/ uop->type & IS_BR_INDIRECT) {
           
        }

        // update retire branch history
        if (uop->type & IS_BR_CONDITIONAL)
            brh_retire = (brh_retire << 1) | (uop->br_taken ? 1 : 0);
        else if (uop_is_branch(uop->type))
            brh_retire = (brh_retire << 1) | 1;
    }
}

void PredictorRunEnd() {
/*
    runs ++;
    if (runs < 2) // set rewind_marked to indicate that we want more runs
        rewind_marked = true;
*/
}

void PredictorExit() {
 }
